package com.atguigu.service.impl;

import com.atguigu.client.ProductFeignClient;
import com.atguigu.constant.RedisConst;
import com.atguigu.entity.CartInfo;
import com.atguigu.entity.SkuInfo;
import com.atguigu.mapper.CartInfoMapper;
import com.atguigu.service.AsyncCartInfoService;
import com.atguigu.service.CartInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 购物车表 用户登录系统时更新冗余 服务实现类
 * </p>
 *
 * @author GuoJH
 * @since 2021-09-04
 */
@Service
public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService {
    @Resource
    private ProductFeignClient productFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AsyncCartInfoService asyncCartInfoService;
    //购物车的添加
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        //判断数据库中是否有相同的添加信息
        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("sku_id", skuId);
        wrapper.eq("user_id", userId);
        //添加时候商品类型只有一个
        CartInfo existCartInfo  = baseMapper.selectOne(wrapper);
        if (existCartInfo  != null){
            //有记录在原先记录上数量加操作
            existCartInfo .setSkuNum(existCartInfo .getSkuNum()+skuNum);
            //也需要实时更新价格
            existCartInfo.setCartPrice(productFeignClient.getSkuPrice(skuId));
            //baseMapper.updateById(existCartInfo);  采用异步方式 提高效率
            asyncCartInfoService.updateCartInfo(existCartInfo);
        }else {
            //没记录则添加
            existCartInfo = new CartInfo();
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            existCartInfo.setSkuId(skuId);
            existCartInfo.setUserId(userId);
            existCartInfo.setCartPrice(skuInfo.getPrice());
            existCartInfo.setSkuNum(skuNum);
            existCartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            existCartInfo.setIsChecked(1); //是否勾选状态
            existCartInfo.setRealTimePrice(productFeignClient.getSkuPrice(skuId));
            existCartInfo.setSkuName(skuInfo.getSkuName());
            //添加数据库
            //baseMapper.insert(existCartInfo);  采用异步方式
            asyncCartInfoService.saveCartInfo(existCartInfo);
        }
        //再往redis中添加一份
        //设置key
        String cartKey = getCartKey(userId);
        //利用hash存储结构
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(),existCartInfo);
        //设置购物车的过期时间
        setCartKeyExpire(cartKey);
    }
    //购物车的列表
    @Override
    public List<CartInfo> getCartList(String userId,String userTempId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        //购物车列表显示 包括登录前 和 登录后
        if (StringUtils.isEmpty(userId)){//userId为空 未登录
            //未登录时的购物车信息
            cartInfoList = getUserCartList(userTempId);
        }
        if (!StringUtils.isEmpty(userId)){//userId 不为空  登录状态
            //查询未登录的购物车信息 方便合并
            List<CartInfo> noLoginCartInfoList = getUserCartList(userTempId);
            if (!CollectionUtils.isEmpty(noLoginCartInfoList)){
                //合并未登录和登录用户购物车信息
                cartInfoList = mergeCartList(noLoginCartInfoList,userId);
                //合并之后删除临时用户id的购物车信息(redis中的)
                deleteNoLoginCartList(userTempId);
            }else{//如果未登录里没有数据 则直接返回登录过的数据 可直接查redis 或者 查DB
                cartInfoList = queryCartInfoFromDbToRedis(userId);
            }
        }
        return cartInfoList;
    }
    //更新商品的选中状态
    @Override
    public void checkCart(String userId,Long skuId, Integer isSelected) {
        //修改状态分为 DB 和 redis
        //DB修改
        asyncCartInfoService.checkCart(userId,skuId,isSelected);
        //redis修改
        String cartKey = getCartKey(userId);
        //根据key获取对应的数据
        BoundHashOperations operations = redisTemplate.boundHashOps(cartKey);
        if (operations.hasKey(skuId.toString())){//如果里面有对应的skuId
            //则进行修改选中状态
            CartInfo redisCartInfo = (CartInfo)operations.get(skuId.toString());
            redisCartInfo.setIsChecked(isSelected);
            //再放入到redis中
            operations.put(skuId.toString(),redisCartInfo);
            //设置过期时间
            setCartKeyExpire(cartKey);
        }
    }
    //购物车中商品的删除
    @Override
    public void deleteCart(String userId, Long skuId) {
        //删除DB
        asyncCartInfoService.deleteCartInfo(userId,skuId);
        //删除redis
        String cartKey = getCartKey(userId);
        BoundHashOperations operations = redisTemplate.boundHashOps(cartKey);
        if (operations.hasKey(skuId.toString())){
            //如果redis中有对应的skuId  则删除
            operations.delete(skuId.toString());
        }
    }
    //查询购物车的选中信息
    @Override
    public List<CartInfo> getSelectedProduct(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        //可从redis中查 减轻DB的压力
        String cartKey = getCartKey(userId);
        //从redis中获取信息返回
        List<CartInfo> redisCartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(redisCartInfoList)){
            for (CartInfo cartInfo : redisCartInfoList) {
                if (cartInfo.getIsChecked() == 1){
                    cartInfoList.add(cartInfo);
                }
            }
        }
        return cartInfoList;
    }

    //合并之后删除临时用户id的购物车信息(redis中的)
    private void deleteNoLoginCartList(String userTempId) {
        /* //删除DB  这里采用异步方式  提高效率
        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
        if (!StringUtils.isEmpty(userTempId)){
            wrapper.eq("user_id",userTempId);
            baseMapper.delete(wrapper);
        }*/
        asyncCartInfoService.deleteCartInfo(userTempId,null);
        //删除redis
        String cartKey = getCartKey(userTempId);
        Boolean aBoolean = redisTemplate.hasKey(cartKey);
        if (aBoolean){
            redisTemplate.delete(cartKey);
        }
    }

    //合并未登录和登录用户购物车信息
    private List<CartInfo> mergeCartList(List<CartInfo> noLoginCartInfoList, String userId) {
        //获取登录过的购物车信息
        List<CartInfo> loginCartInfoList = getUserCartList(userId);
        //对比sku的id是否有相同的 将登录的loginCartInfo 转为map集合 key为skuId,value为cartInfo
        Map<Long, CartInfo> loginCartInfoMap =                                  //单个cartInfo   这个是返回值也为cartInfo
                loginCartInfoList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
        //遍历未登录的购物车信息
        for (CartInfo noLoginCartInfo : noLoginCartInfoList) {
            //得到未登录的skuId
            Long noLoginSkuId = noLoginCartInfo.getSkuId();
            if (loginCartInfoMap.containsKey(noLoginSkuId)){
                //有相同的skuId  则进行加的操作
                CartInfo loginCartInfo = loginCartInfoMap.get(noLoginSkuId);
                loginCartInfo.setSkuNum(loginCartInfo.getSkuNum()+noLoginCartInfo.getSkuNum());
                //同时 在未登录前商品为勾选状态 合并后让其也为勾选状态
                if (noLoginCartInfo.getIsChecked() == 1){
                    loginCartInfo.setIsChecked(1);
                }
                baseMapper.updateById(loginCartInfo);
            }else {
                //不相同 则将临时用户id修改为用户id
                noLoginCartInfo.setUserId(userId);
                baseMapper.updateById(noLoginCartInfo);
            }
        }
        //合并完成之后  再次更新缓存中的数据
        List<CartInfo> cartInfoList = queryCartInfoFromDbToRedis(userId);
        return cartInfoList;
    }

    //购物车的信息
    private List<CartInfo> getUserCartList(String oneOfUserId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        //判断 id(这里的id 可为临时id 也可以为userId实时id)是否为空
        if (StringUtils.isEmpty(oneOfUserId)){
            return cartInfoList;
        }
        //不为空 从数据库中查  再放入redis一份
        cartInfoList = queryCartInfoFromDbToRedis(oneOfUserId);
        return cartInfoList;
    }

    //从DB中查询购物车商品信息  并放入redis中一份
    @Override
    public List<CartInfo> queryCartInfoFromDbToRedis(String oneOfUserId) {
        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",oneOfUserId);
        List<CartInfo> dbCartInfoList = baseMapper.selectList(wrapper);
        //如果 数据库中没有此信息 直接返回
        if (CollectionUtils.isEmpty(dbCartInfoList)){
            return dbCartInfoList;
        }
        //不为空有数据 将集合遍历 放入redis中一份
        String cartKey = getCartKey(oneOfUserId);
        Map<String, CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : dbCartInfoList) {
            //redis中 也需要考虑更新实时价格
            cartInfo.setRealTimePrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));
            //可以单个进行放入 但考虑效率问题 放入到一个map中 直接all添加
            //redisTemplate.boundHashOps(cartKey).put(cartInfo.getSkuId().toString(),cartInfo);
            map.put(cartInfo.getSkuId().toString(),cartInfo);
        }
        redisTemplate.boundHashOps(cartKey).putAll(map);
        //设置购物车过期时间
        redisTemplate.expire(cartKey,RedisConst.USER_CART_EXPIRE,TimeUnit.SECONDS);
        //有数据 则返回
        return dbCartInfoList;
    }

    //购物车的过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    //用户的购物车key
    private String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX+userId+RedisConst.USER_CART_KEY_SUFFIX;
    }
}
